#include "FluidSparseMatrix.h"
#include <algorithm>
#include "mathlib.h"
#include "Utility.h"
#include "DecoLogger.h"



FLuidSparseMatrix::FLuidSparseMatrix() : mIndex3Dto1DMap(NULL)
{

}
FLuidSparseMatrix::FLuidSparseMatrix(int numX, int numY, int numZ, int dim) : mIndex3Dto1DMap(NULL)
{
	SetDimension(numX, numY, numZ, dim);
}

FLuidSparseMatrix::FLuidSparseMatrix(const FLuidSparseMatrix& rhs) : mNumX(rhs.mNumX), mNumY(rhs.mNumY), mNumZ(rhs.mNumZ), mDim(rhs.mDim), mRows(rhs.mRows), mRowValues(mRowValues)
{
	mIndex3Dto1DMap = Malloc3DArray<int>(mNumX, mNumY, mNumZ);
	CopyTo3DArray(mIndex3Dto1DMap, rhs.mIndex3Dto1DMap, mNumX, mNumY, mNumZ);
}

const FLuidSparseMatrix& FLuidSparseMatrix::operator= (const FLuidSparseMatrix& rhs)
{
	if (this == &rhs)
		return *this;

	if (mIndex3Dto1DMap)
		Free3DArray<int>(mIndex3Dto1DMap, mNumX, mNumY, mNumZ);

	mNumX = rhs.mNumX;
	mNumY = rhs.mNumY;
	mNumZ = rhs.mNumZ;
	mDim = rhs.mDim;
	mRows = rhs.mRows;
	mRowValues = mRowValues;
	mIndex3Dto1DMap = Malloc3DArray<int>(mNumX, mNumY, mNumZ);
	CopyTo3DArray(mIndex3Dto1DMap, rhs.mIndex3Dto1DMap, mNumX, mNumY, mNumZ);
	return *this;
}

FLuidSparseMatrix::~FLuidSparseMatrix()
{
	mRows.clear();
	mRowValues.clear();
	if (mIndex3Dto1DMap)
		Free3DArray<int>(mIndex3Dto1DMap, mNumX, mNumY, mNumZ);
	mNumX = mNumY = mNumZ = mDim = 0;

}

int FLuidSparseMatrix::GetDim() const
{
	return mDim;
}
void FLuidSparseMatrix::SetDimension(int numX, int numY, int numZ, int dim)
{
	if (mIndex3Dto1DMap)
		Free3DArray<int>(mIndex3Dto1DMap, mNumX, mNumY, mNumZ);
	mNumX = numX;
	mNumY = numY;
	mNumZ = numZ;
	mDim = dim;
	mIndex3Dto1DMap = Malloc3DArray<int>(numX, numY, numZ);
	SetZero();
}

void FLuidSparseMatrix::SetZero()
{
	mRows.clear();
	mRowValues.clear();
	if (mIndex3Dto1DMap)
		MemSet3DArray<int>(mIndex3Dto1DMap, -1, mNumX, mNumY, mNumZ);

}
double FLuidSparseMatrix::GetValue(int rowI, int rowJ, int rowK, int columnI, int columnJ, int columnK)
{
	if (mIndex3Dto1DMap[rowI][rowJ][rowK] == -1)
	{
		return 0.0;
	}
	else
	{
		int pos = mIndex3Dto1DMap[rowI][rowJ][rowK];
		FluidSparseVector& row = mRowValues[pos];
		return row.GetValue(columnI, columnJ, columnK);
	}
}
void FLuidSparseMatrix::SetValue(int rowI, int rowJ, int rowK, int columnI, int columnJ, int columnK, double value)
{
	if (mIndex3Dto1DMap[rowI][rowJ][rowK] == -1)
	{
		if (abs(value) < 1e-9)
			return;
		FluidSparseVector newRow(mNumX, mNumY, mNumZ, mDim);
		newRow.SetValue(columnI, columnJ, columnK, value);
		SetRow(rowI, rowJ, rowK, newRow);
	}
	else
	{
		int pos = mIndex3Dto1DMap[rowI][rowJ][rowK];
		FluidSparseVector& row = mRowValues[pos];
		return row.SetValue(columnI, columnJ, columnK, value);
	}
}

void FLuidSparseMatrix::SetRow(int rowI, int rowJ, int rowK, const FluidSparseVector& rowValues)
{
	if (mIndex3Dto1DMap[rowI][rowJ][rowK] == -1)
	{
		mRows.push_back(Index3(rowI, rowJ, rowK)); 
		mRowValues.push_back(rowValues);
		mIndex3Dto1DMap[rowI][rowJ][rowK] = static_cast<int>(mRows.size()) - 1;
	}
	else
	{
		int pos = mIndex3Dto1DMap[rowI][rowJ][rowK];
		FluidSparseVector& row = mRowValues[pos];
		row = rowValues;
	}
}

FluidSparseVector& FLuidSparseMatrix::GetRow(int rowI, int rowJ, int rowK)
{
	if (mIndex3Dto1DMap[rowI][rowJ][rowK] == -1)
	{
		mRows.push_back(Index3(rowI, rowJ, rowK));
		mRowValues.push_back(FluidSparseVector(mNumX, mNumY, mNumZ, mDim));
		mIndex3Dto1DMap[rowI][rowJ][rowK] = static_cast<int>(mRows.size()) - 1;
		return mRowValues[mRows.size() - 1];
	}
	else
	{
		int pos = mIndex3Dto1DMap[rowI][rowJ][rowK];
		return mRowValues[pos];
	}
}

bool FLuidSparseMatrix::CheckMMatrix()
{
	int numRows = static_cast<int>(mRows.size());

	for (int i = 0; i < numRows; ++i)
	{
		FluidSparseVector row = mRowValues[i];
		int numCols = static_cast<int>(row.mValues.size());
		double sum = 0;
		for (int j = 0; j < numCols; ++j)
		{
			sum += row.mValues[j];
		}
		//(*DecoLogger::GetSingleton()) << "The sum of " << i << "th row is: " << sum << ".\n";
		if (sum < -0.0001)
			return false;
	}
	return true;
}
bool FLuidSparseMatrix::CheckSymmetry()
{
	int numRows = static_cast<int>(mRows.size());
	bool ret = true;
	for (int i = 0; i < numRows; ++i)
	{
		FluidSparseVector row = mRowValues[i];
		int numCols = static_cast<int>(row.mValues.size());
		for (int j = 0; j < numCols; ++j)
		{
			Index3 rowIndex = mRows[i];
			Index3 colIndex = row.mIndices[j];
			double value = GetValue(colIndex.m_i, colIndex.m_j, colIndex.m_k, rowIndex.m_i, rowIndex.m_j, rowIndex.m_k);
			if (abs(row.mValues[j] - value) > 1e-6)
			{
				(*DecoLogger::GetSingleton()) << "The value of (" << i << ", " << j << ") is: " << row.mValues[j] << ".\n";
				(*DecoLogger::GetSingleton()) << "The value of (" << j << ", " << i << ") is: " << value << ".\n";
				//return false;
				ret = false;
			}
		}
	}
	return ret;
}

const Index3& FLuidSparseMatrix::GetIndex3D(int i) const
{
	return mRows[i];
}


void FLuidSparseMatrix::ConstructTestMatrix()
{
	SetDimension(2, 2, 2, 8);
	FluidSparseVector row(2, 2, 2, 8);
	row.mIndices.push_back(Index3(0, 0, 0));
	row.mValues.push_back(7);
	row.mIndices.push_back(Index3(0, 1, 0));
	row.mValues.push_back(1);
	row.mIndices.push_back(Index3(1, 0, 1));
	row.mValues.push_back(2);
	row.mIndices.push_back(Index3(1, 1, 0));
	row.mValues.push_back(7);
	mRows.push_back(Index3(0, 0, 0));
	mRowValues.push_back(row);

	row.SetZero();
	row.mIndices.push_back(Index3(1, 0, 0));
	row.mValues.push_back(2);
	row.mIndices.push_back(Index3(0, 1, 0));
	row.mValues.push_back(8);
	row.mIndices.push_back(Index3(0, 0, 1));
	row.mValues.push_back(-4);
	mRows.push_back(Index3(0, 0, 1));
	mRowValues.push_back(row);

	row.SetZero();
	row.mIndices.push_back(Index3(0, 1, 0));
	row.mValues.push_back(1);
	row.mIndices.push_back(Index3(1, 1, 1));
	row.mValues.push_back(5);
	mRows.push_back(Index3(0, 1, 0));
	mRowValues.push_back(row);

	row.SetZero();
	row.mIndices.push_back(Index3(0, 1, 1));
	row.mValues.push_back(7);
	row.mIndices.push_back(Index3(1, 0, 1));
	row.mValues.push_back(9);
	mRows.push_back(Index3(0, 1, 1));
	mRowValues.push_back(row);

	row.SetZero();
	row.mIndices.push_back(Index3(1, 0, 1));
	row.mValues.push_back(1);
	row.mIndices.push_back(Index3(1, 0, 0));
	row.mValues.push_back(5);
	row.mIndices.push_back(Index3(1, 1, 0));
	row.mValues.push_back(5);
	mRows.push_back(Index3(1, 0, 0));
	mRowValues.push_back(row);

	row.SetZero();
	row.mIndices.push_back(Index3(1, 0, 1));
	row.mValues.push_back(-1);
	row.mIndices.push_back(Index3(1, 1, 1));
	row.mValues.push_back(5);
	mRows.push_back(Index3(1, 0, 1));
	mRowValues.push_back(row);

	row.SetZero();
	row.mIndices.push_back(Index3(1, 1, 0));
	row.mValues.push_back(11);
	mRows.push_back(Index3(1, 1, 0));
	mRowValues.push_back(row);

	row.SetZero();
	row.mIndices.push_back(Index3(1, 1, 1));
	row.mValues.push_back(5);
	mRows.push_back(Index3(1, 1, 1));
	mRowValues.push_back(row);
}